package ar.edu.unq.wannaplay.services;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import org.joda.time.Interval;
import org.springframework.transaction.annotation.Transactional;

import ar.edu.unq.wannaplay.calendareable.Project;
import ar.edu.unq.wannaplay.client.Client;
import ar.edu.unq.wannaplay.dtos.ClientDTO;
import ar.edu.unq.wannaplay.dtos.EmployeeDTO;
import ar.edu.unq.wannaplay.dtos.ProjectDTO;
import ar.edu.unq.wannaplay.dtos.SkillDTO;
import ar.edu.unq.wannaplay.employee.Employee;
import ar.edu.unq.wannaplay.employee.Skill;
import ar.edu.unq.wannaplay.exceptions.NegativeNumberException;
import ar.edu.unq.wannaplay.exceptions.NullArgumentException;
import ar.edu.unq.wannaplay.helpers.EmployeeRecommender;
import ar.edu.unq.wannaplay.persistence.daos.ProjectDAO;
import ar.edu.unq.wannaplay.utils.Pair;
import ar.edu.unq.wannaplay.utils.Service;

public class ProjectService implements Service<Project, ProjectDTO>, ServiceDeleteDTO<ProjectDTO> {
    private static final long serialVersionUID = 1L;

    private EmployeeService employeeService;

    private EmployeeRecommender employeeRecommender;

    private ClientService clientService;

    private ProjectDAO dao;

    public ProjectDAO getDao() {
        return dao;
    }

    public void setDao(final ProjectDAO dao) {
        this.dao = dao;
    }

    public List<EmployeeDTO> findAllEmployeesDTO() {
        return this.getEmployeeService().findAllDTOs();
    }

    @Transactional
    public List<EmployeeDTO> filterEmployeesByRecommended(final ProjectDTO projectDTO,
            final List<EmployeeDTO> employeesDTO) {

        List<Employee> employees = new ArrayList<Employee>();
        for (EmployeeDTO eDTO : employeesDTO) {
            employees.add(eDTO.fromDto());
        }

        List<Pair<Employee, Float>> recommendedEmployees = employeeRecommender.recommendEmployeesForProject(
                projectDTO.fromDto(), employees);

        List<EmployeeDTO> recommendedEmployeesDTO = new ArrayList<EmployeeDTO>();
        for (Pair<Employee, Float> pair : recommendedEmployees) {
            recommendedEmployeesDTO.add(this.createEmployeeDTO(pair.getFirst()));
        }

        return recommendedEmployeesDTO;
    }

    private EmployeeDTO createEmployeeDTO(final Employee employee) {
        return new EmployeeDTO(employee.getData(), employee.getSkills(), employee.getRegister().getLicences());
    }

    private Project findById(final Serializable id) {
        return this.getDao().findById(id);
    }

    private List<Project> findAll() {
        return this.getDao().findAll();
    }

    // Para datagenerator
    @Transactional
    public void saveProject(final int manHour, final String name, final Client client,
            final List<SkillDTO> requirementsDTO, final Interval interval, final List<EmployeeDTO> employeesDTO)
            throws NegativeNumberException {

        if (manHour < 0) {
            throw new NegativeNumberException("You can't use negative numbers here");
        }

        List<Skill> requirements = new ArrayList<Skill>();
        for (SkillDTO sDTO : requirementsDTO) {
            requirements.add(sDTO.fromDto());
        }

        List<Employee> employees = new ArrayList<Employee>();
        for (EmployeeDTO eDTO : employeesDTO) {
            employees.add(eDTO.fromDto());
        }

        Project project = new Project(name, client, requirements, interval);
        project.setManHour(manHour);
        project.setEmployees(employees);

        this.getDao().saveProject(project);
    }

    @Transactional
    public void saveProject(final int manHour, final String name, final ClientDTO clientDTO,
            final List<SkillDTO> requirementsDTO, final Interval interval, final List<EmployeeDTO> employeesDTO)
            throws NullArgumentException, NegativeNumberException {

        if (manHour < 0) {
            throw new NegativeNumberException("You can't use negative numbers here");
        }

        List<Skill> requirements = new ArrayList<Skill>();
        for (SkillDTO sDTO : requirementsDTO) {
            requirements.add(sDTO.fromDto());
        }

        List<Employee> employees = new ArrayList<Employee>();
        for (EmployeeDTO eDTO : employeesDTO) {
            employees.add(eDTO.fromDto());
        }

        if (clientDTO == null) {
            throw new NullArgumentException("You didn't select a combo element");
        }
        Client client = clientService.findClientById(clientDTO.getId());

        Project project = new Project(name, client, requirements, interval);
        project.setManHour(manHour);
        project.setEmployees(employees);

        this.getDao().saveProject(project);
    }

    @Override
    @Transactional
    public void delete(final ProjectDTO projectDTO) {
        this.getDao().delete(this.fromDto(projectDTO));
    }

    @Transactional
    public void deleteById(final int id) {
        this.getDao().deleteById(id);
    }

    @Transactional
    public List<ProjectDTO> findAllDTOs() {
        return this.toDTOs(this.findAll());
    }

    private List<ProjectDTO> toDTOs(final List<Project> projects) {
        List<ProjectDTO> dtosProjects = new LinkedList<ProjectDTO>();
        for (Project project : projects) {
            dtosProjects.add(this.toDto(project));
        }
        return dtosProjects;
    }

    @Transactional
    public ProjectDTO getProjectDTOById(final String id) {
        return this.toDto(this.findById(Integer.parseInt(id)));
    }

    private Project fromDto(final ProjectDTO entity) {
        return entity.fromDto();
    }

    // Deprecated
    @Transactional
    public void updateProject(final int id, final int manHour, final String name, final ClientDTO clientDTO,
            final List<SkillDTO> requirementsDTO, final Interval interval, final List<EmployeeDTO> employeesDTO) {

        Client client = clientService.findClientById(clientDTO.getId());

        List<Skill> requirements = new ArrayList<Skill>();
        for (SkillDTO sDTO : requirementsDTO) {
            requirements.add(sDTO.fromDto());
        }

        List<Employee> employees = new ArrayList<Employee>();
        for (EmployeeDTO eDTO : employeesDTO) {
            employees.add(eDTO.fromDto());
        }

        Project project = this.getDao().findById(id);

        project.setManHour(manHour);
        project.setName(name);
        project.setClient(client);
        project.setRequirements(requirements);
        project.setInterval(interval);
        project.setEmployees(employees);

        this.getDao().updateProject(project);
    }

    @Transactional
    public void updateProject(final int id, final int manHour, final String name, final Interval interval,
            final ClientDTO clientDTO) throws NullArgumentException, NegativeNumberException {

        if (manHour < 0) {
            throw new NegativeNumberException("You can't use negative numbers here");
        }

        if (clientDTO == null) {
            throw new NullArgumentException("You didn't select a combo element");
        }
        Client client = clientService.findClientById(clientDTO.getId());

        Project project = this.getDao().findById(id);

        project.setManHour(manHour);
        project.setName(name);
        project.setClient(client);
        project.setInterval(interval);

        this.getDao().updateProject(project);
    }

    @Transactional
    public void updateProjectSkills(final int id, final List<SkillDTO> requirementsDTO) {

        List<Skill> requirements = new ArrayList<Skill>();
        for (SkillDTO sDTO : requirementsDTO) {
            requirements.add(sDTO.fromDto());
        }

        Project project = this.getDao().findById(id);
        project.setRequirements(requirements);
        this.getDao().updateProject(project);
    }

    @Transactional
    public void updateProjectEmployees(final int id, final List<EmployeeDTO> employeesDTO) {

        List<Employee> employees = new ArrayList<Employee>();
        for (EmployeeDTO eDTO : employeesDTO) {
            employees.add(eDTO.fromDto());
        }

        Project project = this.getDao().findById(id);
        project.setEmployees(employees);
        this.getDao().updateProject(project);
    }

    @Override
    public ProjectDTO toDto(final Project object) {
        return new ProjectDTO(object);
    }

    public EmployeeRecommender getEmployeeRecommender() {
        return employeeRecommender;
    }

    public void setEmployeeRecommender(final EmployeeRecommender employeeRecommender) {
        this.employeeRecommender = employeeRecommender;
    }

    public EmployeeService getEmployeeService() {
        return employeeService;
    }

    public void setEmployeeService(final EmployeeService employeeService) {
        this.employeeService = employeeService;
    }

    public List<ClientDTO> findAllClientDTOs() {
        return clientService.findAllDTOs();
    }

    public void setClientService(final ClientService clientService) {
        this.clientService = clientService;
    }

    public ClientService getClientService() {
        return clientService;
    }

}
